package feces.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import feces.Database;
import feces.formater.IFormater;
import feces.formater.JSONFormater;
import feces.utility.Utility;

/**
 * RESTful请求伺服对象框架
 * 
 * @author Bromine0x23
 */
public abstract class RESTfulServlet extends HttpServlet {
	
	/**
	 * 字典
	 * 
	 * @author Bromine0x23
	 */
	@NoArgsConstructor(access = AccessLevel.PROTECTED)
	protected static class Dictionary {
		
		public static final String ID = "id";
		
		public static final String PK = "pk";
		
		public static final String COLUMN = "name";
		
		public static final String VALUE = "value";
	}
	
	/**
	 * 响应状态码
	 * 
	 * @author Bromine0x23
	 * 
	 */
	public static enum Status {
		SUCCEED {
			@Override
			public int getCode() {
				return 0;
			}
			
			@Override
			public String getMessage() {
				return "操作成功";
			}
		},
		FAILED {
			@Override
			public int getCode() {
				return 1;
			}
			
			@Override
			public String getMessage() {
				return "操作失败";
			}
		},
		EXCEPTION {
			@Override
			public int getCode() {
				return 2;
			}
			
			@Override
			public String getMessage() {
				return "操作异常";
			}
		},
		INVALID {
			@Override
			public int getCode() {
				return 3;
			}
			
			@Override
			public String getMessage() {
				return "操作非法";
			}
		},
		FORBIDDEN {
			@Override
			public int getCode() {
				return 4;
			}
			
			@Override
			public String getMessage() {
				return "操作被禁止";
			}
		};
		
		public abstract int getCode();
		
		public abstract String getMessage();
	}
	
	/**
	 * 响应包装类
	 * 
	 * @author Bromine0x23
	 *
	 * @param <T> 被包装的数据类型
	 */
	@AllArgsConstructor
	public static class Response<T> {

		@Getter @Setter private int status;
		
		@Getter @Setter private String message;
		
		@Getter @Setter private T data;
	}

	private static final long serialVersionUID = 362780258713957756L;

	public RESTfulServlet() {
		super();
	}
	
	
	
//	@PersistenceUnit
//	private static final EntityManagerFactory factory = Persistence.createEntityManagerFactory("Feces"); 
//
//	@PersistenceContext
//	@Getter(AccessLevel.PROTECTED)
//	private static final EntityManager manager = factory.createEntityManager();
	
	/**
	 * 执行事务
	 *
	 * @param runnable 回调函数，在其中进行事务处理
	 * @return 执行是否成功
	 * @throws IllegalStateException
	 * @throws PersistenceException
	 */
	protected boolean doTransaction(
		Runnable runnable
	) throws PersistenceException {
		return Database.doTransaction(runnable);
//		EntityTransaction transaction = getTransaction();
//		try {
//			transaction.begin();
//			runnable.run();
//			transaction.commit();
//			return true;
//		} catch (IllegalStateException | RollbackException exception){
//			if (transaction.isActive()) {
//				transaction.rollback();
//			}
//			return false;
//		}
	}
	
	/**
	 * 获取事务对象
	 * 
	 * @return 事务对象
	 */
	protected EntityTransaction getTransaction() {
		return Database.getTransaction();
	}
	
	protected CriteriaBuilder getCriteriaBuilder() {
		return Database.getCriteriaBuilder();
	}
	
	/**
	 * 持久化实体数据
	 * 
	 * @param entity 实体
	 */
	protected <E> void add(E entity) {
		Database.add(entity);
	}
	
	/**
	 * 更新实体数据
	 * 
	 * @param entity 实体
	 * @return 更新后的实体
	 */
	protected <E> E update(E entity) {
		return Database.update(entity);
	}
	
	/**
	 * 移除持久化的实体
	 * 
	 * @param entity 实体
	 */
	protected <E> void remove(E entity) {
		Database.remove(entity);
	}
	
	/**
	 * 执行无绑定参数JPQL更新语句
	 * <pre>
	 * Example:
	 * 	executeUpdate("UPDATE e FROM E e set e.x = 1, e.y = 2");
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @return 更新行数
	 */
	protected int executeUpdate(String sql) {
		return Database.executeUpdate(sql);
	}
	
	
	/**
	 * 执行有绑定参数的JPQL更新语句
	 * <pre>
	 * Example:
	 * 	executeUpdate("UPDATE e FROM E e set e.x = ?1, e.y = ?2", 10, 20);
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @param params 绑定参数
	 * @return 更新行数
	 */
	protected <T> int executeUpdate(String sql, @SuppressWarnings("unchecked") T... params) {
		return Database.executeUpdate(sql, params);
	}

	/**
	 * 执行使用命名绑定参数的JPQL更新语句
	 * <pre>
	 * Example:
	 * 	Map<String, Integer> params = new HashMap<String, Integer>();
	 * 	params.put("x", 10);
	 * 	params.put("y", 20);
	 * 	executeUpdate("UPDATE e FROM E e set e.x = :x, e.y = :y", params);
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @param params 命名绑定参数
	 * @return 更新行数
	 */
	protected <T> int executeUpdate(String sql, Map<String, T> params) {
		return Database.executeUpdate(sql, params);
	}

	protected <E> List<E> executeQuery(CriteriaQuery<E> query) {
		return Database.executeQuery(query);
	}
	
	/**
	 * 无绑定参数JPQL查询
	 * <pre>
	 * Example:
	 * 	executeUpdate("SELECT e FROM E e", E.class);
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @param klass 查询结果类型
	 * @return 查询结果
	 */
	protected <E> List<E> executeQuery(String sql, Class<E> klass) {
		return Database.executeQuery(sql, klass);
	}
	
	/**
	 * 有绑定参数的JPQL查询
	 * <pre>
	 * Example:
	 * 	executeUpdate("SELECT DISTINCT e FROM E e WHERE e.x = ?1", E.class, 10);
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @param klass 查询结果类型
	 * @param params 绑定参数
	 * @return 查询结果
	 */
	protected <E, T> List<E> executeQuery(String sql, Class<E> klass, @SuppressWarnings("unchecked") T... params) {
		return Database.executeQuery(sql, klass, params);
	}
	
	/**
	 * 使用命名绑定参数的JPQL查询
	 * <pre>
	 * Example:
	 * 	Map<String, Integer> parameters = new HashMap<String, Integer>();
	 * 	parameters.put("x", 10);
	 * 	executeUpdate("SELECT DISTINCT e FROM E e WHERE e.x = :x", Entity.class, parameters);
	 * </pre>
	 * 
	 * @param sql JPQL语句
	 * @param klass 查询结果类型
	 * @param params 命名绑定参数
	 * @return 查询结果
	 */
	protected <E, T> List<E> executeQuery(String sql, Class<E> klass, Map<String, T> params) {
		return Database.executeQuery(sql, klass, params);
	}
	
	/**
	 * 无绑定参数的JPQL命名查询
	 * 
	 * @param name 查询名
	 * @param klass 查询结果类型
	 * @return 查询结果
	 */
	protected <E> List<E> executeNamedQuery(String name, Class<E> klass) {
		return Database.executeNamedQuery(name, klass);
	}

	/**
	 * 有绑定参数的JPQL命名查询
	 * 
	 * @param name 查询名
	 * @param params 绑定参数
	 * @param klass 查询结果类型
	 * @return 查询结果
	 */
	protected <E, T> List<E> executeNamedQuery(String name, Class<E> klass, @SuppressWarnings("unchecked") T... params) {
		return Database.executeNamedQuery(name, klass, params);
	}

	/**
	 * 使用命名绑定参数的JPQL命名查询
	 * 
	 * @param name 查询名
	 * @param klass 查询结果类型
	 * @param params 命名绑定参数
	 * @return 查询结果
	 */
	protected <E, T> List<E> executeNamedQuery(String name, Class<E> klass, Map<String, T> params) {
		return Database.executeNamedQuery(name, klass, params);
	}

	/**
	 * 查找所有实体
	 * @param klass 实体类类型
	 * @return 所有实体
	 */
	protected <E> List<E> findAll(Class<E> klass) {
		return Database.findAll(klass);
	}
	
	/**
	 * @param klass 实体类类型
	 * @param key 主键
	 * @return 主键对应的实体
	 */
	protected <E, K> E find(Class<E> klass, K key) {
		return Database.find(klass, key);
	}
	
	@Override
	public void init() throws ServletException {
		super.init();
	}
	
	@Override
	public void destroy() {
		super.destroy();
	}
	
	@Override
	public void doDelete(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		writeResponse(request, response, responseDelete(request, response));
	}

	@Override
	public void doGet(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		writeResponse(request, response, responseRead(request, response));
	}

	@Override
	public void doPost(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		writeResponse(request, response, responseCreate(request, response));
	}

	@Override
	public void doPut(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		writeResponse(request, response, responseUpdate(request, response));
	}

	/**
	 * 响应POST请求
	 * @param request 请求对象
	 * @param response 响应对象
	 * @return 响应结果，将被格式化的对象
	 * @throws ServletException
	 * @throws IOException
	 */
	protected Object responseCreate(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		response.sendError(400);
		return null;
	}

	/**
	 * 响应DELETE请求
	 * @param request 请求对象
	 * @param response 响应对象
	 * @return 响应结果，将被格式化的对象
	 * @throws ServletException
	 * @throws IOException
	 */
	protected Object responseDelete(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		response.sendError(400);
		return null;
	}

	/**
	 * 响应GET请求
	 * @param request 请求对象
	 * @param response 响应对象
	 * @return 响应结果，将被格式化的对象
	 * @throws ServletException
	 * @throws IOException
	 */
	protected Object responseRead(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		response.sendError(400);
		return null;
	}

	/**
	 * 响应PUT请求
	 * @param request 请求对象
	 * @param response 响应对象
	 * @return 响应结果，将被格式化的对象
	 * @throws ServletException
	 * @throws IOException
	 */
	protected Object responseUpdate(
		HttpServletRequest request,
		HttpServletResponse response
	) throws ServletException, IOException {
		response.sendError(400);
		return null;
	}
	
	/**
	 * 基本的DELETE响应实现，通过主键删除实体
	 * 
	 * @param request 请求对象
	 * @param klass 实体类
	 * @return 响应结果，是否成功删除
	 */
	protected <E> Response<Boolean> responseDeleteBasic(
		HttpServletRequest request,
		Class<E> klass
	) {
		String parameter = request.getParameter(Dictionary.ID);
		if (parameter == null) {
			return invalid("未设置主键", false);
		}
		Integer id = Utility.toInteger(parameter);
		if (id == null) {
			return invalid("主键非法", false);
		}
		final E e = find(klass, id);
		boolean result = doTransaction(new Runnable() {

			@Override
			public void run() {
				remove(e);
			}

		});
		return result ? succeed("删除成功", true) : exception("删除失败", false);
	}
	
	/**
	 * 基本的GET响应实现，通过主键查找实体
	 * 
	 * @param request 请求对象
	 * @param klass 实体类
	 * @return 响应结果，是否成功删除
	 */
	protected <E> Response<E> responseReadBasic(
		HttpServletRequest request,
		Class<E> klass
	) {
		String parameter = request.getParameter(Dictionary.ID);
		if (parameter == null) {
			return invalid(null);
		}
		Integer id = Utility.toInteger(parameter);
		if (id == null) {
			return invalid(null);
		}
		E e = find(klass, id);
		if (e != null) {
			return succeed(e);
		}
		return failed(null);
	}
	
	/**
	 * 包装响应
	 * 
	 * @param status 响应状态码
	 * @param data 数据
	 * @return 包装后的响应
	 */
	protected <T> Response<T> response(Status status, T data) {
		
		return new Response<T>(status.getCode(), status.getMessage(), data);
	}
	
	/**
	 * 包装响应
	 * 
	 * @param status 响应状态码
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 */
	protected <T> Response<T> response(Status status, String message, T data) {
		
		return new Response<T>(status.getCode(), message, data);
	}
	
	/**
	 * 包装操作成功响应
	 * 
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> succeed(T data) {
		return response(Status.SUCCEED, data);
	}
	
	/**
	 * 包装操作失败响应(可预知错误)
	 * 
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> failed(T data) {
		return response(Status.FAILED, data);
	}
	
	/**
	 * 包装操作异常响应(不可预知错误)
	 * 
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> exception(T data) {
		return response(Status.EXCEPTION, data);
	}
	
	/**
	 * 包装操作非法响应(参数非法等)
	 * 
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> invalid(T data) {
		return response(Status.INVALID, data);
	}
	
	/**
	 * 包装操作禁止响应(无权限)
	 * 
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> forbidden(T data) {
		return response(Status.FORBIDDEN, data);
	}
	
	/**
	 * 包装操作成功响应
	 * 
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> succeed(String message, T data) {
		return response(Status.SUCCEED, message, data);
	}
	
	/**
	 * 包装操作失败响应(可预知错误)
	 * 
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> failed(String message, T data) {
		return response(Status.FAILED, message, data);
	}
	
	/**
	 * 包装操作异常响应(不可预知错误)
	 * 
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> exception(String message, T data) {
		return response(Status.EXCEPTION, message, data);
	}
	
	/**
	 * 包装操作非法响应(参数非法等)
	 * 
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> invalid(String message, T data) {
		return response(Status.INVALID, message, data);
	}
	
	/**
	 * 包装操作禁止响应(无权限)
	 * 
	 * @param message 响应消息
	 * @param data 数据
	 * @return 包装后的响应
	 * @see RESTfulServlet#response(Status, Object)
	 */
	protected <T> Response<T> forbidden(String message, T data) {
		return response(Status.FORBIDDEN, message, data);
	}
	
	
	private IFormater getFormatter(HttpServletRequest request) {
		Object object = request.getAttribute(IFormater.KEY_FORMAT);
		if (object instanceof IFormater) {
			return (IFormater) object;
		}
		return new JSONFormater();
	}
	
	private <T> void writeResponse(
		HttpServletRequest request,
		HttpServletResponse response,
		T data
	) throws IOException {
		IFormater formatter = getFormatter(request);
		try (PrintWriter writer = response.getWriter()) {
			response.setContentType(formatter.contentType());
			formatter.format(data, writer);
			writer.flush();
		}
	}
}
